package com.lonewalker.sparrow.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.lonewalker.sparrow.annotation.AvoidReSubmit;
import com.lonewalker.sparrow.exception.ServiceException;
import com.lonewalker.sparrow.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;


/**
 * @author LoneWalker
 * @version 4.0
 */
@Slf4j
@Aspect
@Component
public class AvoidReSubmitAspect {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 环绕通知
     *
     */
    @Around("@within(avoidReSubmit) || @annotation(avoidReSubmit)")
    public Object around(ProceedingJoinPoint joinPoint, AvoidReSubmit avoidReSubmit) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        //获取用户id
        long userId = 111L;
        //用于记录成功或者失败
        boolean res = false;
        //防重提交类型
        String type = avoidReSubmit.limitType().name();
        if (type.equalsIgnoreCase(AvoidReSubmit.Type.PARAM.name())) {
            //方式一，参数形式防重提交
            long lockTime = avoidReSubmit.lockTime();
            String ipAddr = IpUtil.getIp(request);
            MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            String className = method.getDeclaringClass().getName();

            String key = MD5.create().digestHex16(String.format("%s-%s-%s-%s",ipAddr,className,method,userId), StandardCharsets.UTF_8);
            res  = Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(key, "1", lockTime, TimeUnit.SECONDS));
        } else {
            //方式二，令牌形式防重提交
            String requestToken = request.getHeader("request-token");
            if (StrUtil.isBlank(requestToken)) {
                throw new ServiceException("获取token失败");
            }
            String key = String.format("%s-%s", userId, requestToken);
            res = Boolean.TRUE.equals(stringRedisTemplate.delete(key));
        }
        if (!res) {
            throw new ServiceException("请勿重复提交");
        }
        return joinPoint.proceed();
    }

}
